/*
  ivl_hyper.c  -  implementation of hyberbolic functions and their inverses

  This file is part of the IVL Library (LIBIVL).
  
  LIBIVL - library for interval computations 
  
  Copyright (c) 2007 Mateusz Paprocki

  The IVL Library is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.

  The IVL Library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with the IVL Library; see the file LICENCE.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "interval.h"

int ivl_sinh(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {	
		inexact_left = mpfr_sinh(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
		inexact_right = mpfr_sinh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);
		
		IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);		
	}
	
	return inexact;
}

int ivl_cosh(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {
		if (mpfr_sgn(IVL_RIGHT(op)) >= 0) {					
			if (mpfr_sgn(IVL_LEFT(op)) >= 0) {
				inexact_left = mpfr_cosh(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
				inexact_right = mpfr_cosh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);
			} else {
				inexact_left = mpfr_set_ui(IVL_LEFT(ivl), 1, GMP_RNDD);
				
				if (mpfr_cmpabs(IVL_LEFT(op), IVL_RIGHT(op)) > 0)
					inexact_right = mpfr_cosh(IVL_RIGHT(ivl), IVL_LEFT(op), GMP_RNDU);
				else
					inexact_right = mpfr_cosh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);
			}			
		} else {
			inexact_left = mpfr_cosh(IVL_LEFT(ivl), IVL_RIGHT(op), GMP_RNDD);
			inexact_right = mpfr_cosh(IVL_RIGHT(ivl), IVL_LEFT(op), GMP_RNDU);
		}
							
		IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);
	}
	
	return inexact;
}

int ivl_tanh(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {	
		inexact_left = mpfr_tanh(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
		inexact_right = mpfr_tanh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);

		IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);
	}
	
	return inexact;
}

int ivl_csch(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {	
		if (ivl_has_zero(ivl))
			ivl_make_unbounded(ivl, IVL_PARTIAL);
		else {
			inexact_left = mpfr_csch(IVL_LEFT(ivl), IVL_RIGHT(op), GMP_RNDD);
			inexact_right = mpfr_csch(IVL_RIGHT(ivl), IVL_LEFT(op), GMP_RNDU);
		
			IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);
		}
	}
	
	return inexact;
}

int ivl_sech(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {
		if (mpfr_sgn(IVL_RIGHT(op)) >= 0) {					
			if (mpfr_sgn(IVL_LEFT(op)) >= 0) {
				inexact_left = mpfr_sech(IVL_LEFT(ivl), IVL_RIGHT(op), GMP_RNDD);
				inexact_right = mpfr_sech(IVL_RIGHT(ivl), IVL_LEFT(op), GMP_RNDU);
			} else {
				inexact_left = mpfr_set_ui(IVL_RIGHT(ivl), 1, GMP_RNDU);
				
				if (mpfr_cmpabs(IVL_LEFT(op), IVL_RIGHT(op)) < 0)
					inexact_right = mpfr_sech(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
				else
					inexact_right = mpfr_sech(IVL_LEFT(ivl), IVL_RIGHT(op), GMP_RNDD);
			}			
		} else {
			inexact_left = mpfr_sech(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
			inexact_right = mpfr_sech(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);
		}
							
		IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);
	}
	
	return inexact;
}

int ivl_coth(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {	
		if (ivl_has_zero(ivl))
			ivl_make_unbounded(ivl, IVL_PARTIAL);
		else {
			inexact_left = mpfr_coth(IVL_LEFT(ivl), IVL_RIGHT(op), GMP_RNDD);
			inexact_right = mpfr_coth(IVL_RIGHT(ivl), IVL_LEFT(op), GMP_RNDU);
		
			IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);
		}
	}
	
	return inexact;
}

int ivl_asinh(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {	
		inexact_left = mpfr_asinh(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
		inexact_right = mpfr_asinh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);

		IVL_CHECK(ivl, op->domain, inexact, inexact_left, inexact_right);
	}
	
	return inexact;
}

int ivl_acosh(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(op) || (mpfr_cmp_ui(IVL_RIGHT(op), 1) < 0))
		ivl->domain = IVL_UNDEFINED;
	else {				
		if (mpfr_cmp_ui(IVL_LEFT(op), 1) >= 0) {
			inexact_left = mpfr_acosh(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);	
			ivl->domain = op->domain;
		} else {
			inexact_left = mpfr_set_ui(IVL_LEFT(ivl), 0, GMP_RNDD);	
			ivl->domain = IVL_PARTIAL;
		}
		
		inexact_right = mpfr_acosh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);

		IVL_CHECK_NO_DOM(ivl, inexact, inexact_left, inexact_right);		
	}
	
	return inexact;
}

int ivl_atanh(ivl_ptr ivl, ivl_srcptr op) {
	int inexact = 0, inexact_left = 0, inexact_right = 0;
	
	if (DOM_UNDEFINED(op))
		ivl->domain = IVL_UNDEFINED;
	else {				
		if (mpfr_cmp_si(IVL_LEFT(op), -1) <= 0) {
			if (mpfr_cmp_si(IVL_RIGHT(op), -1) >= 0) {				
				mpfr_set_inf(IVL_LEFT(ivl), -1);
				ivl->domain = IVL_PARTIAL;
				
				if (mpfr_cmp_ui(IVL_RIGHT(op), 1) >= 0)
					mpfr_set_inf(IVL_RIGHT(ivl), 1);
				else
					inexact_right = mpfr_atanh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);									
			} else
				ivl->domain = IVL_UNDEFINED;			
		} else {
			inexact_left = mpfr_atanh(IVL_LEFT(ivl), IVL_LEFT(op), GMP_RNDD);
			
			if (mpfr_cmp_si(IVL_RIGHT(op), 1) < 0) {
				inexact_right = mpfr_atanh(IVL_RIGHT(ivl), IVL_RIGHT(op), GMP_RNDU);								
				ivl->domain = op->domain;
			} else {
				mpfr_set_inf(IVL_RIGHT(ivl), 1);
				ivl->domain = IVL_PARTIAL;				
			}
		}

		IVL_CHECK_NO_DOM(ivl, inexact, inexact_left, inexact_right);		
	}
	
	return inexact;
}

